package com.rtsapp.server.network.server;

import com.rtsapp.server.module.AbstractModule;
import com.rtsapp.server.module.Application;
import com.rtsapp.server.module.Module;
import com.rtsapp.server.network.session.Session;
import com.rtsapp.server.network.session.netty.NettySessionManager;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.Future;
import com.rtsapp.server.logger.Logger;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;


public  class NettyServer extends AbstractModule {

	private static final Logger LOGGER =com.rtsapp.server.logger.LoggerFactory.getLogger( NettyServer.class );
	
	private NettyServerCfg serverCfg;
	private Application application ;
	private ChannelInitializer<?> channelInitializer;
	private NettySessionManager sessionManager;
	
	
	private ServerBootstrap server ;
	private EventLoopGroup bossGroup ;
	private EventLoopGroup workerGroup;
	
	public NettyServer(  ){
		sessionManager = new NettySessionManager( Session.SessionType.ServerSession );
	}
	
	public void setServerCfg( NettyServerCfg serverCfg ){
		this.serverCfg = serverCfg;
		sessionManager.setSessionLimit( serverCfg.getSessionLimit() );
	}
	
	public void setChannelInitializer( ChannelInitializer<?> channlInitializer ){
		this.channelInitializer = channlInitializer;
	}
	
	
	public NettySessionManager getSessionManager( ){
		return this.sessionManager;
	}
	
	
	public boolean initialize( Module parentModule ){
		
		LOGGER.info("NettyServer " + this.getName() + " initialize ");
		
		this.application = (  Application )parentModule;
		
		
		if( this.channelInitializer instanceof NettyServerChannelInitializer ){
	  		
	  		NettyServerChannelInitializer nettyChannelInitializer = ( NettyServerChannelInitializer )this.channelInitializer;
	  		if( ! nettyChannelInitializer.initialize( this.application, sessionManager ) ){

				LOGGER.error( " NettyServer " + this.getName() + " initialize fail " );
				return false;
			}
	  	}

		LOGGER.info( " NettyServer " + this.getName() + " initialize success " );
		return true;
	}


	public boolean start( ){
		
	
		server = new ServerBootstrap();
		
		if( serverCfg.isLinux( ) ){
			
			EpollEventLoopGroup linuxBossGroup = new EpollEventLoopGroup( serverCfg.getBossThread() , new DefaultThreadFactory( this.getName() + ":boss" ));
			EpollEventLoopGroup linuxWorkerGroup = new EpollEventLoopGroup( serverCfg.getWorkerThread() , new DefaultThreadFactory( this.getName() + ":worker" ) );
			linuxWorkerGroup.setIoRatio( serverCfg.getIoRatio() );
			
			bossGroup = linuxBossGroup;
			workerGroup = linuxWorkerGroup;

			server.group( bossGroup, workerGroup ).channel( EpollServerSocketChannel.class);
		}else{
			NioEventLoopGroup nioBossGroup = new NioEventLoopGroup( serverCfg.getBossThread() , new DefaultThreadFactory( this.getName() + ":boss" ) );
			NioEventLoopGroup nioWorkerGroup = new NioEventLoopGroup( serverCfg.getWorkerThread(), new DefaultThreadFactory( this.getName() + ":worker" ) );
			nioWorkerGroup.setIoRatio( serverCfg.getIoRatio() );
			
			bossGroup = nioBossGroup;
			workerGroup = nioWorkerGroup;
			
			server.group( bossGroup, workerGroup ).channel( NioServerSocketChannel.class );
		}

	 	// 使用缓冲的ByteBufAllocator
	 	// 禁用Nagle算法
		server.option(  ChannelOption.SO_BACKLOG, 128 )
				.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
				.childOption( ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT )
				.option( ChannelOption.TCP_NODELAY, true)
				.childOption(ChannelOption.TCP_NODELAY, true );

		server.childHandler( this.channelInitializer );

		try{
			
			ChannelFuture f =  server.bind( serverCfg.getPort() ).sync();
			
			LOGGER.info("Server " + this.getName()+ " start at :" + serverCfg.getPort());
			return true;
		}catch(Throwable e ){
			LOGGER.error("Server start error:" + this.getName(), e);
			return false;
		}
		
		
	}
	
	public void stop( ){

		LOGGER.info("NettyServer " + this.getName() + "  stoping ");

		//关闭连接监听
		Future<?> bossFuture = bossGroup.shutdownGracefully(100, 100, TimeUnit.MILLISECONDS);

		//关闭所有
		List<Session> sessions =  sessionManager.getAllSessions();
		for( Session session : sessions ){
			session.flush( );
			session.close( );
		}

		Future<?> workerFuture  = workerGroup.shutdownGracefully( 100, 100, TimeUnit.MILLISECONDS );

		try {
			bossFuture.await();
			workerFuture.await();

			LOGGER.info("NettyServer " + this.getName() + "  stoped ");
		} catch (InterruptedException e) {
			LOGGER.error("Server stop error:" + this.getName(), e);
		}
		
	}
	
	public void destroy( ){
		LOGGER.info("Server destroy:" + this.getName());
	}

	
}
